/* Copyright 2017, 2018, 2019, 2020 Gabriel Czernikier
 *
 * This file is part of Côtehaus.
 * Côtehaus is free software: you can redistribute it and/or modify
 * it under the terms of one of the following:
 * 1. The same dual license as that of Côtehaus itself, of which individual licenses are
 * the MIT License and the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 2. The MIT License.
 * 3. The GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * Côtehaus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * MIT License and the GNU General Public License for more details.
 * You should have received a copy of the MIT License and the GNU General Public License
 * along with Côtehaus.  If not, see <https://opensource.org/licenses/MIT> and <http://www.gnu.org/licenses/>.
 */
#define _BSD_SOURCE

#include <endian.h>
#include <stdint.h>
#include <string.h>
#include <wchar.h>
#include <polarssl/sha1.h>
#include <zlib.h>
#include "composer.h"
#include "types.h"

LIST_HEAD(header_head);

void
build_header() {
	// Nearly nothing to do here.
	majors_size_ary[NH_HEADER_IDX] = 1;
	bounds_move(NH_HEADER_IDX, NH_HEADER_UP_BOUND);
}

void
pack_header() {
	// Make a header's chunk to an extent that neither the checksum (Adler32), the digest (Sha-1), nor the file_size are required.
	// Then make the checksum and the digest, prepend them to the larger chunk then prepend packedly the last few fields.
	buffer[NH_HEADER_IDX] = malloc(NH_HEADER_UP_BOUND);
	buf_len[NH_HEADER_IDX] = NH_HEADER_UP_BOUND;
	memset(buffer[NH_HEADER_IDX], 0, NH_HEADER_UP_BOUND);
	// file_size
	uint32_t packed = htole32((uint32_t)up_bounds_ary[NH_UP_BOUNDS_SZ-1]);
	memcpy(&buffer[NH_HEADER_IDX][32], &packed, sizeof(uint32_t));
	// header_size
	packed = htole32((uint32_t)NH_HEADER_UP_BOUND);
	memcpy(&buffer[NH_HEADER_IDX][36], &packed, sizeof(uint32_t));
	// endian_tag
	packed = htobe32((uint32_t)0x78563412);
	memcpy(&buffer[NH_HEADER_IDX][40], &packed, sizeof(uint32_t));
	// TODO link_size
	// TODO link_off
	// map_off
	packed = htole32((uint32_t)up_bounds_ary[NH_MAP_LIST_IDX-1]);
	memcpy(&buffer[NH_HEADER_IDX][52], &packed, sizeof(uint32_t));
	// string_ids_size
	packed = htole32((uint32_t)majors_size_ary[NH_STR_ID_IDX]);
	memcpy(&buffer[NH_HEADER_IDX][56], &packed, sizeof(uint32_t));
	// string_ids_off
	packed = htole32((uint32_t)majors_size_ary[NH_STR_ID_IDX]!=0?up_bounds_ary[NH_STR_ID_IDX-1]:0);
	memcpy(&buffer[NH_HEADER_IDX][60], &packed, sizeof(uint32_t));
	// type_ids_size
	packed = htole32((uint32_t)majors_size_ary[NH_TYPE_ID_IDX]);
	memcpy(&buffer[NH_HEADER_IDX][64], &packed, sizeof(uint32_t));
	// type_ids_off
	packed = htole32((uint32_t)majors_size_ary[NH_TYPE_ID_IDX]!=0?up_bounds_ary[NH_TYPE_ID_IDX-1]:0);
	memcpy(&buffer[NH_HEADER_IDX][68], &packed, sizeof(uint32_t));
	// proto_ids_size
	packed = htole32((uint32_t)majors_size_ary[NH_PROTO_ID_IDX]);
	memcpy(&buffer[NH_HEADER_IDX][72], &packed, sizeof(uint32_t));
	// proto_ids_off
	packed = htole32((uint32_t)majors_size_ary[NH_PROTO_ID_IDX]!=0?up_bounds_ary[NH_PROTO_ID_IDX-1]:0);
	memcpy(&buffer[NH_HEADER_IDX][76], &packed, sizeof(uint32_t));
	// TODO more fields
	// data_size
	packed = htole32((uint32_t)up_bounds_ary[NH_DATA_UBOUND]-up_bounds_ary[NH_DATA_LBOUND]);
	memcpy(&buffer[NH_HEADER_IDX][104], &packed, sizeof(uint32_t));
	// data_off
	packed = htole32((uint32_t)up_bounds_ary[NH_DATA_LBOUND]);
	memcpy(&buffer[NH_HEADER_IDX][108], &packed, sizeof(uint32_t));
	
	// signature
	sha1_context *ctx = malloc(sizeof(sha1_context));
	sha1_init(ctx);
	sha1_starts(ctx);
	sha1_update(ctx, &buffer[NH_HEADER_IDX][32], NH_HEADER_UP_BOUND-32);
	for(int i=NH_HEADER_IDX+1; i!=NH_UP_BOUNDS_SZ; i++) {
		sha1_update(ctx, buffer[i], buf_offset[i]);
	}
	sha1_finish(ctx, &buffer[NH_HEADER_IDX][12]);
	sha1_free(ctx);
	// checksum
	uLong adler = adler32(0L, Z_NULL, 0);
	adler = adler32(adler, &buffer[NH_HEADER_IDX][12], NH_HEADER_UP_BOUND-12);
	for(int i=NH_HEADER_IDX+1; i!=NH_UP_BOUNDS_SZ; i++) {
		adler = adler32(adler, buffer[i], buf_offset[i]);
	}
	packed = htole32((uint32_t)adler);
	memcpy(&buffer[NH_HEADER_IDX][8], &packed, sizeof(uint32_t));
	// magic
	static const char magic[] = {0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x38, 0x00};
	memcpy(buffer[NH_HEADER_IDX], &magic, 8);
}
